﻿namespace Contoso.ActiveDirectory {

    #region Usings
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Text;
    using System.Threading.Tasks; 
    #endregion

    /// <summary>
    /// Provides overloads to read big endian streams
    /// </summary>
    internal class EndianBinaryReader : BinaryReader {

        internal EndianBinaryReader(Stream baseStream) : base(baseStream) { }

        internal byte PeekByte() {
            byte Value = base.ReadByte();
            base.BaseStream.Seek(-1, SeekOrigin.Current);
            return Value;
        }

        internal byte[] ReadBytes(int count, bool isBigEndian) {
            if (!isBigEndian) return base.ReadBytes(count);
            return base.ReadBytes(count).Reverse().ToArray();
        }

        internal IPAddress ReadIPAddress() {
            return IPAddress.Parse(
                string.Format("{0}.{1}.{2}.{3}",
                base.ReadByte(),
                base.ReadByte(),
                base.ReadByte(),
                base.ReadByte()));
        }

        internal IPAddress ReadIPv6Address() {
            return IPAddress.Parse(
                string.Format("{0:X}:{1:X}:{2:X}:{3:X}:{4:X}:{5:X}:{6:X}:{7:X}",
                this.ReadUInt16(isBigEndian: true),
                    this.ReadUInt16(isBigEndian: true),
                    this.ReadUInt16(isBigEndian: true),
                    this.ReadUInt16(isBigEndian: true),
                    this.ReadUInt16(isBigEndian: true),
                    this.ReadUInt16(isBigEndian: true),
                    this.ReadUInt16(isBigEndian: true),
                    this.ReadUInt16(isBigEndian: true)));
        }

        internal ushort ReadUInt16(bool isBigEndian) {
            if (!isBigEndian) return base.ReadUInt16();
            return (ushort)((base.ReadByte() << 8) | base.ReadByte());
        }

        internal uint ReadUInt32(bool isBigEndian) {
            if (!isBigEndian) return base.ReadUInt32();
            return (uint)(
                (base.ReadByte() << 24) |
                (base.ReadByte() << 16) |
                (base.ReadByte() << 8) |
                base.ReadByte());
        }

        internal ulong ReadUInt64(bool isBigEndian) {
            if (!isBigEndian) return base.ReadUInt64();
            return (ulong)(
                (base.ReadByte() << 56) |
                (base.ReadByte() << 48) |
                (base.ReadByte() << 40) |
                (base.ReadByte() << 32) |
                (base.ReadByte() << 24) |
                (base.ReadByte() << 16) |
                (base.ReadByte() << 8) |
                base.ReadByte());
        }
    }
}
